package msu.ml.data;

import msu.ml.core.*;
import msu.ml.data.level2.*;
import msu.ml.util.*;
import java.sql.*;
import java.io.*;
import java.util.regex.*;
import java.util.*;
import weka.core.*;

import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.xpath.*;

/**
 * The IDataLoader interface is used to load/parse an instances object
 * from a file. By using the isSupportedData(String) method, a set of
 * IDataLoader objects can be examined, looking for one with the ability
 * to load the file you are asking for.
 *
 * @author Reginald M Mead
 * @version 1.0
 */
public class DatabaseDataLoader 
{
   private String localDataDir;
   protected DatabaseModel database;

   public DatabaseDataLoader(String url, String user, String pass)
   {
      database = new DatabaseModel(url, user, pass);
   }

   public void setLocalDataDirectory(String path)
   {
      this.localDataDir = path;
   }

   public String getLocalDataDirectory()
   {
      return this.localDataDir;
   }

   /**
    * Returns true if the data loader can load the specified file
    * 
    * @param target file to load
    * @return true if the fiel is of a type that can be loaded
    */
   public boolean isSupportedData(String target)
   {
      if(!(target.endsWith(".Z") || target.endsWith(".gz")))
         return false;

      if(database.lookupVolumeId(target) != -1)
         return true;


      if(this.localDataDir != null && 
            new File(this.localDataDir + File.separatorChar + 
               target).exists())
      {
         return true;
      }

      return false;
   }

   public NxInstances load(String target,
         ArrayList<IDataPreProcessor> filters)
   {
      if(!isSupportedData(target))
         return null;

      if(database.lookupVolumeId(target) == -1)
         preCache(target, "0");

      int limit = 0;
      boolean invert = false;

      String [] indices = new String[0];

      for(IDataPreProcessor filter : filters)
      {
         if(filter instanceof SubsamplePreProcessor)
         {
            SubsamplePreProcessor sub = (SubsamplePreProcessor)filter; 
            double pct = sub.getPercentage();
            limit = (int)(database.pulseVolumeCount(target) * pct);
            sub.setEnabled(false);
         }
         else if(filter instanceof AttributeRemovalPreProcessor)
         {
            AttributeRemovalPreProcessor attr =
               (AttributeRemovalPreProcessor)filter;
            invert = attr.getInvertSelection();
            indices = attr.getAttributeIndices().split(",");
            attr.setEnabled(false);
         }
      }
      FilteredPulseVolumeSQLAdapter adapter = 
         new FilteredPulseVolumeSQLAdapter(limit);

      if(!invert)
      {
         adapter.includeFeature(FilteredPulseVolumeSQLAdapter.ALL);
         adapter.excludeFeature(FilteredPulseVolumeSQLAdapter.PULSE_ID);
         for(int i = 0; i < indices.length; i++)
         {
            try
            {
               adapter.excludeFeature(Integer.parseInt(indices[i]));
            }catch(Exception e){}
         }
      }
      else
      {
         adapter.includeFeature(FilteredPulseVolumeSQLAdapter.NONE);
         for(int i = 0; i < indices.length; i++)
         {
            try
            {
               adapter.includeFeature(Integer.parseInt(indices[i]));
            }catch(Exception e){}
         }
      }


      NxInstances instances = null;
      for(IPulseVolume pulse : database.getPulseVolumes(target, adapter))
      {
         if(instances == null)
         {
            String [] attributes = pulse.getFeatureNames();
            FastVector v = new FastVector(attributes.length);
            for(int i = 0; i < attributes.length - 1; i++)
               v.addElement(new Attribute(attributes[i]));
            FastVector classes = new FastVector(2);
            classes.addElement("0");
            classes.addElement("1");
            v.addElement(new Attribute("class", classes));

            instances = new NxInstances(target, v, 1024);
         }

         instances.add(new Instance(1.0, pulse.getFeatures()));
      }

      instances.setSourceFile(target);
      instances.setClassIndex(instances.numAttributes() - 1);
      return instances;
   }

   public NxInstances load(String target)
   {

      return load(target, new ArrayList<IDataPreProcessor>());
   }

   public void preCache(String target, String sweepClass)
   {

      VolumeReader reader = new VolumeReader(new BlockReader(target));
      Volume v = reader.readVolume();
      reader.close();

      PreProcessor pp = new PreProcessor();
      pp.setMinRange(20000);

      Scan rscan = pp.process(v.getSweep(0).getScan(0));
      Scan dscan = pp.process(v.getSweep(0).getScan(1));

      int volume_id = database.insertVolume(target.substring(target.length() - 25));
      int sweep_id = database.insertSweep(volume_id, sweepClass);

      ScanDataConverter converter = new ScanDataConverter();
      Instances data = converter.convertToInstances(target, rscan, dscan,
         true, true, true);

      for(int i = 0; i < data.numInstances(); i++)
         database.insertPulseVolume(sweep_id, true, data.instance(i));

      return;

   }

   /**
    * Load the file and return an NxInstances representation of it.
    * 
    * @param target file to load
    * @return the NxInstances loaded from file
    */
   /*
   public void preCache(String target)
   {
      ElevationCut cut = new ElevationCut(
            this.localDataDir + File.separatorChar + target, 0.5f);
      cut.setPulseVolumeFactory(new ExtendedPulseVolumeFactory(25, 1000));

      int volume_id = database.insertVolume(target);
      int sweep_id = database.insertSweep(volume_id, 
            DataUtility.lookupClass(this.localDataDir + File.separatorChar + target));

      for(int i = 0; i < cut.getNumSweeps(); i++)
         database.insertScan(sweep_id, cut.getSweep(i), i);

      PulseVolumeEnumeration pve = (PulseVolumeEnumeration)cut.getPulseVolumes();
      pve.setMinimumRange(20000);
      pve.setMaximumRange(145000);
      pve.setPulseDepth(1000);

      if(!pve.hasMoreElements())
         return;

      do
      {
         ExtendedPulseVolume pulse = 
            (ExtendedPulseVolume)pve.nextElement();
         if(pulse != null && pulse.isValidPulseVolume())
            database.insertPulseVolume(sweep_id, pulse);
      }
      while(pve.hasMoreElements());

      return;
   }*/
}
